/***********************************************
*	名称:HTTP请求
*	说明:HTTP请求的封装
*	作者:钱纯净
************************************************/
package https

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"strings"
	"time"
)

//type Long int
//优化get请求不能请求大文件的问题

type Interface interface {
	Client(*http.Client) Interface
	New(string) Interface
	Url(string) Interface
	AddHead(string, string) Interface
	AddHeads(map[string]string) Interface
	SetHeads(map[string]string) Interface
	SetAuthorization(string) Interface
	SetToken(string) Interface
	SetTimeout(int) Interface
	AddQuery(string, interface{}) Interface
	AddQuerys(map[string]interface{}) Interface
	SetQuerys(map[string]interface{}) Interface
	Req(interface{}) Interface
	Request(interface{}) Interface
	Res(interface{}) Interface
	Response(interface{}) Interface
	Debug(...int) Interface

	Get() error
	Post() error
	Put() error
	Delete() error
	Do(string) error
}

type Http struct {
	client     *http.Client           //请求客户端
	reuse      bool                   //重用客户端,未实现
	url        string                 //请求连接
	heads      map[string]string      //请求头
	querys     map[string]string      //query
	req        interface{}            //请求参数,body
	res        interface{}            //响应参数
	Json       map[string]interface{} //json里面读取对应的参数,未实现
	debug      bool                   //是否打印日志
	debugModel int                    //日志模式,0取消打印,1是简略,2是详细
	spend      string                 //请求时间消耗
	//log    []*HttpLog        //请求日志,单例模式下有效
	//mu sync.RWMutex //锁
}

//请求模式
func New(url string) Interface {
	data := Http{
		client: NewClient(),
	}
	return data.New(url)
}

//重置参数,heads,querys,请求参数,响应参数
func (this Http) New(url string) Interface {
	this.url = url
	this.heads = map[string]string{
		"Content-Type": "application/json;charset=utf-8",
		"Accept":       "application/json",
		"Connection":   "close",
	}
	this.querys = make(map[string]string)
	this.req = nil
	this.res = nil
	return this
}

//自定义客户端
func (this Http) Client(v *http.Client) Interface {
	if v == nil {
		this.client = NewClient()
		return this
	}
	this.client = v
	return this
}

func (this Http) Debug(n ...int) Interface {
	this.debug = true
	this.debugModel = 99
	if len(n) > 0 {
		this.debugModel = n[0]
	}
	return this
}

//重置url
func (this Http) Url(s string) Interface {
	this.url = s
	return this
}

//添加请求头
func (this Http) AddHead(key, value string) Interface {
	this.heads[key] = value
	return this
}

//批量添加请求头
func (this Http) AddHeads(m map[string]string) Interface {
	for i, v := range m {
		this.heads[i] = v
	}
	return this
}

//设置请求头,覆盖
func (this Http) SetHeads(m map[string]string) Interface {
	this.heads = m
	return this
}

//设置token
func (this Http) SetAuthorization(val string) Interface {
	this.heads["Authorization"] = val
	return this
}

//设置token
func (this Http) SetToken(val string) Interface {
	this.heads["Authorization"] = val
	return this
}

func (this Http) AddQuery(key string, v interface{}) Interface {
	this.querys[key] = toString(v)
	return this
}

func (this Http) AddQuerys(m map[string]interface{}) Interface {
	for i, v := range m {
		this.AddQuery(i, v)
	}
	return this
}

func (this Http) SetQuerys(m map[string]interface{}) Interface {
	l := make(map[string]string)
	for i, v := range m {
		l[i] = toString(v)
	}
	this.querys = l
	return this
}

//设置超时,单位秒
func (this Http) SetTimeout(n int) Interface {
	this.client.Timeout = time.Second * time.Duration(n)
	return this
}

//请求数据
func (this Http) Req(v interface{}) Interface {
	this.req = v
	return this
}

//请求数据
func (this Http) Request(v interface{}) Interface {
	this.req = v
	return this
}

//响应数据
func (this Http) Res(v interface{}) Interface {
	this.res = v
	return this
}

//响应数据
func (this Http) Response(v interface{}) Interface {
	this.res = v
	return this
}

//////////////////////////////////////////////////////

//////////////////////////////////////////////////////

func (this Http) Get() error {
	return this.Do(http.MethodGet)
}

func (this Http) Post() error {
	return this.Do(http.MethodPost)
}

func (this Http) Put() error {
	return this.Do(http.MethodPut)
}

func (this Http) Delete() error {
	return this.Do(http.MethodDelete)
}

func (this Http) Do(method string) error {
	res := this.do(method)
	if res.Err() != nil {
		return res.err
	}
	if res.Code() < 200 || res.Code() >= 300 {
		return errors.New(fmt.Sprintf(`
		%s:%d
		%s`, string([]byte{231, 138, 182, 230, 128, 129, 231, 160, 129}), res.Code(), string(res.Body())))
	}
	return nil
}

//----------------------------------------

func (this Http) do(method string) Response {
	t := time.Now()
	bs, err := this.reqByte()
	if err != nil {
		return newResponseErr(err)
	}
	reqest, err := http.NewRequest(method, dealQuery(this.url, this.querys), bytes.NewReader(bs))
	this.SetQuerys(map[string]interface{}{})
	if err != nil {
		return newResponseErr(err)
	}
	defer func(req *http.Request) {
		req.Close = true
	}(reqest)
	defer reqest.Body.Close()
	for i, v := range this.heads {
		//这样可以设置小写的请求头
		reqest.Header[i] = []string{v}
	}
	if this.client == nil {
		this.client = NewClient()
	}
	res, err := this.client.Do(reqest)
	if err != nil {
		return newResponseErr(err)
	}
	defer res.Body.Close()
	this.spend = "耗时:" + time.Now().Sub(t).String()
	this.print(reqest, string(bs), res)
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return newResponseErr(err)
	}
	if this.res != nil && len(body) != 0 {
		switch val := this.res.(type) {
		case *string:
			*val = string(body)
		case *[]byte:
			*val = body
		default:
			if err := json.Unmarshal(body, this.res); err != nil {
				return newResponseErr(err)
			}
		}
	}
	return newResponse(res)
}

//特殊处理byte数组和字符串
func (this Http) reqByte() ([]byte, error) {
	var bs []byte
	var err error
	switch val := this.req.(type) {
	case string:
		bs = []byte(val)
	case *string:
		bs = []byte(*val)
	case []byte:
		bs = val
	case *[]byte:
		bs = *val
	default:
		bs, err = json.Marshal(this.req)
	}
	return bs, err
}

/////////////////////////////////////////

//拼接query参数的连接
func dealQuery(url string, m map[string]string) string {
	if len(m) != 0 {
		n := strings.Index(url, "?")
		if n == -1 {
			url += "?"
			for i, v := range m {
				url += i + "=" + v + "&"
			}
		} else {
			for i, v := range m {
				url += "&" + i + "=" + v
			}
		}
	}
	return url
}

//打印日志
func (this Http) print(req *http.Request, body string, res *http.Response) {
	if this.debug {
		switch this.debugModel {
		case 0:
		case 1:
			if res.StatusCode > 300 {
				fmt.Println(this.spend, res.StatusCode, " ", req.Method, req.URL, body)
			}
			fmt.Println(this.spend, res.StatusCode, req.Method, req.URL)
		case 2:
			this.defPrint2(req, body, res)
		default:
			this.defPrint(req, body, res)
		}
	}
}

//默认详细日志
func (this Http) defPrint(req *http.Request, body string, res *http.Response) {
	if this.debug {
		a, _ := httputil.DumpResponse(res, true)
		fmt.Println("----------------------------------------")
		fmt.Println(req.Method, "\t", req.URL)
		if body != "null" {
			fmt.Println(body)
		}
		fmt.Println("----------------------------------------")
		fmt.Println(string(a))
		fmt.Println("----------------------------------------")
	}
}

func (this Http) defPrint2(req *http.Request, body string, res *http.Response) {
	if this.debug {
		a, _ := httputil.DumpResponse(res, true)
		fmt.Println("----------------------------------------")
		fmt.Println(req.Method, "\t", req.URL)
		fmt.Println()
		for i, v := range req.Header {
			fmt.Println(i + ":" + strings.Join(v, " "))
		}
		fmt.Println()
		if body != "null" {
			fmt.Println(body)
		}
		fmt.Println("----------------------------------------")
		fmt.Println(string(a))
		fmt.Println("----------------------------------------")
	}
}
